package cn.com.dbappsecurity.frame.handler;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Enumeration;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileUploadException;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;

import cn.com.dbappsecurity.dao.LogMapper;
import cn.com.dbappsecurity.entity.Log;
import cn.com.dbappsecurity.entity.User;
import cn.com.dbappsecurity.frame.Constant;
import cn.com.dbappsecurity.frame.annotations.Auth;
import cn.com.dbappsecurity.frame.datamodal.IDataModal;
import cn.com.dbappsecurity.frame.datamodal.JsonDataModal;
import cn.com.dbappsecurity.frame.datamodal.PageDataModal;
import cn.com.dbappsecurity.frame.exception.FrameException;
import cn.com.dbappsecurity.frame.request.EncodingRequest;
import cn.com.dbappsecurity.frame.request.MultipartRequest;
import cn.com.dbappsecurity.frame.scanner.Actions;
import cn.com.dbappsecurity.frame.scanner.Controllers;
import cn.com.dbappsecurity.utils.CookieUtil;
import cn.com.dbappsecurity.utils.LocaleUtil;
import cn.com.dbappsecurity.utils.MybatisUtil;

/**
 * Contraoller调用处理器
 * @author dhz
 */
public class ContraollerHandler{

	protected static final Logger LOG = Logger.getLogger(ContraollerHandler.class);
	/**
	 * 保存在session中的locale key
	 */
	public static final String SESSION_LOCAL_KEY = "locale";
	
	public void execute(Controllers controllers,Actions actions,
			HttpServletRequest req,HttpServletResponse resp) {
		
		cn.com.dbappsecurity.frame.annotations.Log logAnn = null;
		boolean operSuccess = false;
		try {
			if(controllers == null || actions == null){
				throw new FrameException(FrameException.TYPE_REQ_ADDR_NOT_FOUND);
			}
			Method method = actions.getMethod();
			logAnn = method.getAnnotation(cn.com.dbappsecurity.frame.annotations.Log.class);
			//权限检查
			if(!perssionCheck(method.getAnnotation(Auth.class),req)){
				String msg = LocaleUtil.getMessage(FrameException.TYPE_NOT_PERSSION,
						CookieUtil.getValue(req.getCookies(),Constant.LANGUAGE_KEY));
				new JsonDataModal(false,msg).shipToResponse(req, resp);
				return;
			}
			req = wrapHttpRequestFile(req,actions.getMaxLen());
			//调用方法
			Object[] args;
			try {
				args = actions.assemParam(req,resp);
			} catch (Exception e) {
				LOG.error("",e);
				throw new FrameException(FrameException.TYPE_ARGU_ERROR);
			}
			Object result = method.invoke(controllers.getDoInstance(),args);
			if(result != null){
				IDataModal modal = null;
				if(result instanceof IDataModal){
					modal = (IDataModal) result;
				}else if(result instanceof Boolean){
					modal = new JsonDataModal((Boolean)result);
				}else if(result instanceof String){
					modal = new PageDataModal(true,(String)result);
				}
				if(modal != null){
					operSuccess = modal.isSuccess();
					modal.shipToResponse(req,resp);
				}
			}else{
				operSuccess = true;
			}
		} catch (Throwable e){
			while(e instanceof InvocationTargetException){
				e = ((InvocationTargetException)e).getTargetException();
			}
			String msg = LocaleUtil.getMessage(e,CookieUtil.getValue(req.getCookies(),Constant.LANGUAGE_KEY));
			IDataModal responseModal = new JsonDataModal(false,msg);
			responseModal.shipToResponse(req,resp);
			LOG.error("控制器调用出错",e);
		} finally {
			//记录操作日志
			addLog(logAnn,req,operSuccess);
		}
	}
	
	/*
	 * 包装request 如果存在上传文件则返回MultipartRequest(处理文件上传),
	 * 如果为GET方式请求则返回EncodingRequest(处理中文乱码)
	 * @param request
	 * @param maxSize
	 * @return
	 * @throws Exception
	 */
	protected HttpServletRequest wrapHttpRequestFile(HttpServletRequest req,double maxSize) throws UnsupportedEncodingException, FileUploadException{
		String type = req.getHeader("Content-Type");
		//multipart提交要特殊处理
		if(type != null && type.contains("multipart/form-data")){
			return new MultipartRequest(req,(long)maxSize,Constant.ENCODING);
		}
		//get请求处理中文乱码
		else if("GET".equalsIgnoreCase(req.getMethod())){
			return new EncodingRequest(req,"UTF-8");
		}
		//不作处理
		else{
			return req;
		}
	}

	/**
	 * 权限检查
	 * @param auth
	 * @param request
	 * @return
	 */
	protected boolean perssionCheck(Auth auth,HttpServletRequest request){
		if(auth != null){
			request.getSession().getAttribute(Constant.AUTH_PRIVILEGE);
			
		}
		return true;
	}
	
	/**
	 * 记录操作日志
	 * @param actions
	 * @param request
	 */
	@SuppressWarnings("unchecked")
	protected void addLog(cn.com.dbappsecurity.frame.annotations.Log logAnn,HttpServletRequest request,boolean operSuccess) {
		if(logAnn != null){
			StringBuffer descr = new StringBuffer();
			String[] entry = logAnn.entry();
			if(entry.length > 0){
				for(String en : entry){
					String[] enArray = en.split("=");
					descr.append(',');
					descr.append(enArray[1]);
					descr.append('=');
					String val = request.getParameter(enArray[0]);
					descr.append(val==null?"":val);
				}
			}else{
				Enumeration<String> enums = request.getParameterNames();
				while(enums.hasMoreElements()){
					String name = enums.nextElement();
					descr.append(',');
					descr.append(name);
					descr.append('=');
					String val = request.getParameter(name);
					descr.append(val==null?"":val);
				}
			}
			if(descr.length() > 0){
				descr.deleteCharAt(0);
			}
			
			User user = null;
			try {
				user = (User)request.getSession().getAttribute(Constant.AUTH_KEY);
			} catch (Exception e) {
				LOG.error("多半是因为session失效，不算啥子问题！",e);
			}
			
			Log log = new Log();
			log.setDesct(descr.toString());
			log.setEvent(logAnn.value());
			log.setStatus(operSuccess?1:2);
			log.setIp(request.getRemoteAddr());
			log.setUsername(user == null ? "" : user.getName());
			
			try {
				SqlSession sqlSession = MybatisUtil.sqlSessionFactory.openSession(true);
				LogMapper logMapper = sqlSession.getMapper(LogMapper.class);
				int num = logMapper.insert(log);
				if(num != 1){
					LOG.error("操作日志记录失败");
				}
			} catch (Exception e) {
				LOG.error("操作日志记录失败",e);
			}
		}
	}
}
